package libgin

import (
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"

	"gitee.com/goframe/lt-collector/libs/libce"
)

//middleware auth
//generate jwt token,cache it;
//verify jwt token
type Map map[string]interface{}

type (
	JWT struct {
		Key    []byte
		Bearer string
	}
)

func TokenFormatWithPrefix(prefix string, uid int64) string {
	return fmt.Sprintf("token%s_uid_%d", prefix, uid)
}

func (j *JWT) Auth(fns ...func(c *gin.Context)) gin.HandlerFunc {
	return func(c *gin.Context) {
		if err := j.auth(c); err != nil {
			c.Abort()
			Return.GinSet(c, http.StatusUnauthorized, err.Error())
			return
		}
		if len(fns) > 0 {
			fns[0](c)
		}
		c.Next()
	}
}

func (j *JWT) auth(c *gin.Context) (err error) {
	tsg := strings.Replace(c.GetHeader(HeaderAuthorization), j.Bearer, "", 1)
	mp := Map{}
	if err = j.Bind(tsg, &mp); err != nil {
		return
	}
	uid := int64(mp["uid"].(float64))
	val, has := libce.Cache.Get(TokenFormatWithPrefix("", uid))
	if !has {
		return errors.New("令牌无效")
	}
	if tsg != val.(string) {
		return errors.New("令牌已过期")
	}
	c.Set("Uid", uid)
	return
}

func (j *JWT) GenerateByUID(uid int64) (string, error) {
	claims := Map{"uid": uid, "ns": time.Now().UnixNano()}
	return j.generate(jwt.MapClaims(claims))
}

func (j *JWT) GetUID(tsg string) (uid int64, err error) {
	mp := Map{}
	if err = j.bind(tsg, &mp); err != nil {
		return
	}
	uid = int64(mp["uid"].(float64))
	return
}

func (j *JWT) Generate(claims Map) (string, error) {
	return j.generate(jwt.MapClaims(claims))
}

func (j *JWT) Bind(tsg string, claims *Map) error {
	return j.bind(tsg, claims)
}

func (j *JWT) generate(claims jwt.MapClaims) (tsg string, err error) {
	tkn := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tsg, err = tkn.SignedString(j.Key)
	return
}

func (j *JWT) bind(tsg string, claims *Map) (err error) {
	tsg = strings.Replace(tsg, j.Bearer, "", 1)
	var token *jwt.Token
	token, err = jwt.Parse(tsg, func(tkn *jwt.Token) (interface{}, error) {
		if tkn.Method.Alg() != "HS256" {
			return nil, fmt.Errorf("unexpected signing method: %v", tkn.Header["alg"])
		}
		return j.Key, nil
	})
	if err != nil {
		return
	}

	cs, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return errors.New("令牌类型不匹配")
	}
	if err = token.Claims.Valid(); err != nil {
		return
	}
	*claims = Map(cs)
	return
}

func NewJWT(key []byte) *JWT {
	return &JWT{Key: key, Bearer: "Bearer "}
}
